from tkinter import N
import pyperclip
import psutil
import pygetwindow
import win32process
import win32gui
import win32con
import win32api
import win32clipboard
import ctypes
import time
import os

import lxlog
# https://pyautogui.readthedocs.io/en/latest/keyboard.html
import pyautogui

from PIL import Image
from io import BytesIO

# 根据className和name获取窗口句柄
def FindWindow(classname=None, name=None) -> int:
    return win32gui.FindWindow(classname, name)

# 打开窗口
def OpenWindow(window=None, classname=None, name=None) -> int:
    if window is None or window == 0:
        window = FindWindow(classname, name)
    if window == 0:
        raise Exception("窗口未找到")
    win32gui.ShowWindow(window, 1)
    # 激活窗口
    # win32gui.SetForegroundWindow(window)
    # 设置窗口最前
    win32gui.SetWindowPos(window, -1, 0, 0, 0, 0, 3)
    win32gui.SetWindowPos(window, -2, 0, 0, 0, 0, 3)
    return window

def RollIntoView(win, ele, equal=False):
    if ele.BoundingRectangle.top < win.BoundingRectangle.top:
        # 上滚动
        while True:
            win.WheelUp(wheelTimes=1, waitTime=0.1)
            if ele.BoundingRectangle.top >= win.BoundingRectangle.top:
                break

    elif ele.BoundingRectangle.bottom >= win.BoundingRectangle.bottom:
        # 下滚动
        while True:
            win.WheelDown(wheelTimes=1, waitTime=0.1)
            if equal:
                if ele.BoundingRectangle.bottom <= win.BoundingRectangle.bottom:
                    break
            else:
                if ele.BoundingRectangle.bottom < win.BoundingRectangle.bottom:
                    break
def EnterWindow(window): # 模拟按下 Enter 键
    if window:
        # 模拟按下 Enter 键
        win32api.keybd_event(win32con.VK_RETURN, 0, 0, 0)
        # 模拟释放 Enter 键
        win32api.keybd_event(win32con.VK_RETURN, 0, win32con.KEYEVENTF_KEYUP, 0)

# 最小化窗口
def MinimizeWindow(window=None, classname=None, name=None):
    if window is None or window == 0:
        window = FindWindow(classname, name)
    if window == 0:
        raise Exception("窗口未找到")
    win32gui.ShowWindow(window, win32con.SW_MINIMIZE)
    return window
# 关闭窗口
def CloseWindow(window=None, classname=None, name=None):
    if window is None or window == 0:
        window = FindWindow(classname, name)
    if window == 0:
        raise Exception("窗口未找到")
    # 向窗口发送关闭消息
    win32gui.PostMessage(window, win32con.WM_CLOSE, 0, 0)
    return window

# 遍历指定窗口下所有控件
# wx_window = uia.WindowControl(Name="企业微信", className="WeWorkWindow")
# print_controls(wx_window.GetParentControl())
def print_controls(control, indent=0):
    print(' ' * indent + f'{control.ControlTypeName}: {control.Name}')
    for child in control.GetChildren():
        print_controls(child, indent + 4)

# 定义根据窗口标题查找窗口句柄的函数
def find_window_handle(window_title,exe_name=None):
    def callback(hwnd, extra):
        try:
            # 获取窗口标题
            title = win32gui.GetWindowText(hwnd)
            class_name = win32gui.GetClassName(hwnd)
            # 获取进程名
            _, pid = win32process.GetWindowThreadProcessId(hwnd)
            process = psutil.Process(pid)
            process_name = process.name()
            # 确保是企业微信的进程
            if window_title == title:
                lxlog.debug(f"找到窗口: {title} (PID: {pid}) - 进程名: {process_name} - 类名: {class_name}")
                if exe_name is None or process_name.lower() == exe_name.lower():
                    extra.append(hwnd)
        except Exception:
            pass  # 忽略任何错误
            
    hwnds = []
    win32gui.EnumWindows(callback, hwnds)
    if hwnds:
        return hwnds[0]
    return None

def force_activate_window(hwnd, delay=0.1):
    """强制激活窗口"""
    try:
        lxlog.debug(f"尝试强制激活窗口: {hwnd}")
        # # 获取窗口所属的进程ID
        # _, pid = win32process.GetWindowThreadProcessId(hwnd)
        # # 获取进程句柄
        # process_handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, False, pid)
        # # 设置进程优先级
        # win32process.SetPriorityClass(process_handle, win32process.HIGH_PRIORITY_CLASS)
        # # 强制显示窗口
        win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE)
        time.sleep(delay)   
        win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
        time.sleep(delay)
        # win32gui.SetForegroundWindow(hwnd)
        # 将窗口移到前台
        # win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0,  win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
        # time.sleep(delay)
        # win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, 0, 0, 0, 0,  win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
        
        # 模拟 Alt 键按下和释放，以确保窗口激活
        win32api.keybd_event(win32con.VK_MENU, 0, 0, 0)
        time.sleep(delay)
        win32gui.SetForegroundWindow(hwnd)
        time.sleep(delay)
        win32api.keybd_event(win32con.VK_MENU, 0, win32con.KEYEVENTF_KEYUP, 0)
        return True
    except Exception as e:
        lxlog.error(f"激活窗口失败: {str(e)}")
        return False

def get_window_active(window_name, exe_name=None, max_retries=1, retry_delay=0.5):
    # 尝试多次获取窗口
    for attempt in range(max_retries):
        lxlog.info(f"正在获取{window_name}窗口...(尝试 {attempt + 1}/{max_retries})")
        hwnd = find_window_handle(window_name,exe_name)
        if hwnd:
            try:
                window = pygetwindow.Window(hwnd)
                if (win32gui.GetForegroundWindow() == hwnd and window.isActive):
                    return window, hwnd
                force_activate_window(hwnd)
                time.sleep(0.2)
                if (win32gui.GetForegroundWindow() == hwnd and window.isActive):
                    return window, hwnd
            except Exception as e:
                lxlog.error(f"验证窗口失败: {str(e)}")
        lxlog.debug(f"等待重试... ({attempt + 1}/{max_retries})")
        time.sleep(retry_delay)
    return None, None

def clear_search_box(sleep=0.5):
    # 清空搜索框内容，确保焦点在搜索框内
    lxlog.debug("清空搜索框...")
    pyautogui.hotkey('ctrl', 'a')
    time.sleep(0.3)
    pyautogui.press('backspace')
    time.sleep(sleep)

#复制消息到剪切板粘贴
def paste_message(message, sleep=0.5):
    lxlog.debug(f"粘贴文本: {message}")
    pyperclip.copy(message)
    pyautogui.hotkey('ctrl', 'v')
    time.sleep(sleep)

# 复制消息到剪切板粘贴
def set_clipboard_text(message):
    pyperclip.copy(message)

# 获取剪切板中的文字
def get_clipboard_text():
    try:
        text = pyperclip.paste()
        return text
    except Exception as e:
        lxlog.error(f"获取剪切板文本失败: {str(e)}")
        return None

def change_window(window,hwnd,w,h,x=None,y=None, sleep=1.5):
    lxlog.debug("调整窗口大小...")
    if x is None:
        x = window.left
    if y is None:
        y = window.top
    win32gui.MoveWindow(hwnd, x, y, w, h, True)
    time.sleep(sleep)

def move_mouse_window(window,x,y, sleep=0.5):
    lxlog.debug(f"移动鼠标x:{x} y:{y}")
    # 获取窗口的位置和大小
    window_left = window.left
    window_top = window.top
    window_width = window.width
    window_height = window.height
    lxlog.debug(f"窗口位置和大小:left={window_left}, top={window_top}, width={window_width}, height={window_height}")

    # 计算相对于窗口的坐标
    relative_x = window_left + x
    relative_y = window_top + y
    pyautogui.moveTo(relative_x, relative_y)   # moves mouse to X of 100, Y of 200.
    time.sleep(sleep)
    return relative_x, relative_y

# 利用微信截图找到要点击的位置
def click_window(window,x,y, sleep=1):
    lxlog.debug(f"点击窗口x:{x} y:{y}")
    relative_x, relative_y = move_mouse_window(window,x,y)
    pyautogui.click(relative_x, relative_y)
    time.sleep(sleep)

# 双击
def double_click_window(window,x,y, sleep=1):
    lxlog.debug(f"双击窗口x:{x} y:{y}")
    relative_x, relative_y = move_mouse_window(window,x,y)
    pyautogui.doubleClick(relative_x, relative_y)
    time.sleep(sleep)

# 右键点击
def right_click_window(window,x,y, sleep=1):
    lxlog.debug(f"双击窗口x:{x} y:{y}")
    relative_x, relative_y = move_mouse_window(window,x,y)
    pyautogui.rightClick(relative_x, relative_y)
    time.sleep(sleep)

# 点击
def click(x,y,clicks=1,button='left' ,sleep=1):
    lxlog.debug(f"点击窗口x:{x} y:{y}")
    pyautogui.click(x, y,clicks=clicks,button=button ,interval=0.1,duration=0.1)
    time.sleep(sleep)

class DROPFILES(ctypes.Structure):
    _fields_ = [
    ("pFiles", ctypes.c_uint),
    ("x", ctypes.c_long),
    ("y", ctypes.c_long),
    ("fNC", ctypes.c_int),
    ("fWide", ctypes.c_bool),
    ]

pDropFiles = DROPFILES()
pDropFiles.pFiles = ctypes.sizeof(DROPFILES)
pDropFiles.fWide = True
matedata = bytes(pDropFiles)


def copy_files_to_clipboard(paths):
    for file in paths:
        if not os.path.exists(file):
            raise FileNotFoundError(f"file ({file}) not exists!")
    files = ("\0".join(paths)).replace("/", "\\")
    data = files.encode("U16")[2:]+b"\0\0"
    t0 = time.time()
    while True:
        if time.time() - t0 > 10:
            raise TimeoutError(f"设置剪贴板文件超时！ --> {paths}")
        try:
            win32clipboard.OpenClipboard()
            win32clipboard.EmptyClipboard()
            win32clipboard.SetClipboardData(win32clipboard.CF_HDROP, matedata+data)
            break
        except:
            pass
        finally:
            try:
                win32clipboard.CloseClipboard()
            except:
                pass

def copy_img_to_clipboard(img_path):
    # 打开并处理图片
    image = Image.open(img_path)
    # 将图片复制到剪贴板
    output = BytesIO()
    image.convert('RGB').save(output, 'BMP')
    data = output.getvalue()[14:]
    output.close()
    win32clipboard.OpenClipboard()
    win32clipboard.EmptyClipboard()
    win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data)
    win32clipboard.CloseClipboard()

# 检查指定区域是否有非白色区域
def check_search_result(x , y , w, h,full_search_window=None): 
    if full_search_window:
        # 获取窗口的位置和大小
        window_left = full_search_window.left
        window_top = full_search_window.top
        window_width = full_search_window.width
        window_height = full_search_window.height
        # 计算搜索结果区域的位置(搜索框下方区域)
        lxlog.debug(f"搜索结果区域位置:left={window_left}, top={window_top}, width={window_width}, height={window_height}")
        x = window_left + x # 左边距10%
        y = window_top + y  # 顶部距离10%
    # 获取搜索结果区域的截图
    try:
        screenshot = pyautogui.screenshot(region=(x,  y, w,  h))   
        # 保存截图用于调试(可选)
        screenshot.save('image/check_search_result.png')
        # 检查截图中的像素颜色变化来判断是否有搜索结果
        # 将图片转换为灰度值列表
        pixels = list(screenshot.convert('L').getdata())
        # 计算非白色像素的数量(假设背景是白色)
        non_white_pixels = sum(1 for p in pixels if p < 240)  # 250是阈值，可以调整
        
        # 如果非白色像素超过一定比例，认为有搜索结果
        threshold = len(pixels) * 0.1  # 10%的像素变化阈值，可以调整
        lxlog.debug(f"非白色像素数量: {non_white_pixels}, 阈值: {threshold}")
        if non_white_pixels > threshold:
            lxlog.debug("找到搜索结果")
            return True
        else:
            lxlog.debug("未找到搜索结果")
            return False
            
    except Exception as e:
        lxlog.error(f"检查搜索结果时出错: {e}")
        return False

def screenshot(imageFilename='image/screenshot.png'):
    try:
        pyautogui.screenshot(imageFilename=imageFilename) #截图保存
    except Exception as e:
        lxlog.info(f"截图失败: {e}")
def locate_on_screen(img_path, grayscale=False, confidence=0.5,region=None, center=True , log=True):
     # 获取指定图片位置
    try:
        pyautogui.screenshot(imageFilename=f'image/locate_{img_path}', region=region) #截图保存
        if center:
            # 计算按钮中心点
            button_pos = pyautogui.locateCenterOnScreen(img_path, grayscale=grayscale, confidence=confidence, region=region)
            if button_pos:
                #lxlog.info(f"匹配到目标:{img_path} {button_pos}")
                return int(button_pos.x), int(button_pos.y)
        else:
            button_pos = pyautogui.locateOnScreen(img_path, grayscale=grayscale, confidence=confidence, region=region)
            if button_pos:
                #lxlog.info(f"匹配到目标:{img_path} {button_pos}")
                return int(button_pos.left), int(button_pos.top)
    except Exception as e:
        if log:
            lxlog.info(f"未匹配到目标:{img_path} {e}")
    return None,None

# 点击指定图片位置
def click_window_by_image(img_path, grayscale=False, confidence=0.9, sleep=0.5,region=None, log=True):
    try:
        if log:
            pyautogui.screenshot(imageFilename=f'image/click_{img_path}', region=region) #截图保存
        button_center = pyautogui.locateCenterOnScreen(img_path, grayscale=grayscale, confidence=confidence, region=region)
        if button_center and button_center.x:
               # 移动并点击
            pyautogui.moveTo(button_center.x, button_center.y, duration=0.1)
            pyautogui.click()
            time.sleep(sleep)
            return True
        elif log:
            lxlog.info(f"未匹配到目标")
    except Exception as e:
        if log:
            lxlog.info(f"未匹配到目标1: {e}")
    return False

# 根据图片链接下载图片并拷贝
def url_image_to_clipboard(img_url):
    lxlog.debug(f"正在处理图片:{img_url}")
    try:
        image_path = img_url
        temp_path = None
        # 检查是否是URL
        if img_url.startswith(('http://', 'https://')):
            import requests
            from urllib.parse import urlparse
            # 使用session下载图片，增加超时时间
            session = requests.Session()
            session.trust_env = False  # 禁用环境变量中的代理设置
            # 获取用户的下载文件夹路径
            download_folder = os.path.join(os.path.expanduser('~'), 'Downloads')
            response = session.get(img_url, timeout=30)
            if response.status_code == 200:
                # 从URL中提取文件名
                filename = os.path.basename(urlparse(img_url).path)
                temp_path = os.path.join(download_folder, 'temp_' + filename)
                # 保存临时文件
                with open(temp_path, 'wb') as f:
                    f.write(response.content)
                # 使用临时文件路径
                image_path = temp_path
            else:
                err_msg = f"下载图片失败: HTTP {response.status_code}"
                return None
        copy_img_to_clipboard(image_path)
        lxlog.debug(f"已将图片复制到剪贴板: {image_path}")
        # 粘贴图片
        pyautogui.hotkey('ctrl', 'v')
        time.sleep(1)
        # 删除临时文件
        if temp_path:
            try:
                os.remove(temp_path)
            except:
                pass
    except Exception as e:
        err_msg = f"处理图片时出错: {str(e)}"
        #send_message(err_msg)
        lxlog.error(err_msg)
